#include <hw3_common.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <math.h>

void safe(cudaError_t err, int lineNum){
	if (err != cudaSuccess){
		printf("Error at %d\n", lineNum) ;
		printf("%s\n", cudaGetErrorString(err)) ;
		exit(-1) ;
	}
}

double wall_clock(){	//Return seconds from the epoch
	struct timeval tv ;
	gettimeofday(&tv, NULL) ;
	return tv.tv_sec + tv.tv_usec * 1e-6 ;
}

void init(int *numCudaDevices, struct cudaDeviceProp *props, bool dispinfo){
	safe(cudaGetDeviceCount(numCudaDevices), __LINE__) ;

	if (dispinfo)
		printf("There are %d cuda devices\n", *numCudaDevices) ;
	
	safe(cudaSetDevice(0), __LINE__) ;
	
	safe(cudaGetDeviceProperties(props, 0), __LINE__) ;

	if (dispinfo){
		printf("Number of MultiProcessors: %d\n", props->multiProcessorCount) ;
		printf("Max threads per block: %d\n", props->maxThreadsPerBlock) ;
		printf("Max thrad dimensions: (%d, %d, %d)\n", props->maxThreadsDim[0], props->maxThreadsDim[1], props->maxThreadsDim[2]) ;
		printf("Max grid dimensions: (%d, %d, %d)\n", props->maxGridSize[0], props->maxGridSize[1], props->maxGridSize[2]) ;
		printf("Name :%s\n", props->name) ;
		printf("Compute capability:%d . %d\n", props->major, props->minor) ;
	}
}

void validateDimensions(dim3 dimGrid, dim3 dimBlock, int n){
	if (dimGrid.x * dimBlock.x > n || dimGrid.y * dimBlock.y > n){
		printf("Too few data!\n") ;	//not enough data for each thread
		exit(-1) ;
	}

	if (n % (dimGrid.x * dimBlock.x) != 0 || n % (dimGrid.y * dimBlock.y) != 0){
		printf("Invalid dimensions!\n") ;
		exit (-1) ;
	}

	if (dimGrid.x != dimGrid.y || dimBlock.x != dimBlock.y){	//Wolog, we assume squares
		printf("Dimensions do not match!\n") ;
		exit(-1) ;
	}
}

void genData(float *matrix, int size){
	int i ;
	srand(time(NULL)) ;
	for (i = 0; i < size; i++){
		matrix[i] = ((float)rand()) / RAND_MAX ;
	}
}

void cpuMult(float *matrix, float *res, int n){	//Only for A * transpose(A)
	int i, j ;
	for (i = 0; i < n; i++){
		for (j = 0; j < n; j++){
			float accu = 0 ;
			int k ;
			for (k = 0; k < n; k++)
				accu += matrix[i * n + k] * matrix[j * n + k] ;
			res[i * n + j] = accu ;
		}
	}
}

void printMatrix(float *matrix, int n){
	int i, j ;
	for (i = 0; i < n; i++){
		for (j = 0; j < n; j++)
			printf("%.4f, ", matrix[i * n + j]) ;
		printf("\n") ;
	}
	printf("\n") ;
}

float compare(float *matrix1, float *matrix2, int n){
	float residual = 0 ;
	int i ;
	for (i = 0; i < n * n; i++)
		residual += fabs(matrix2[i] - matrix1[i]) ;
	return residual / n / n ;
}
